/**
 * 
 * @Project le cOeur - Data Virtualization
 * @Date 08.12.2012
 * @Author Aebi Pascal, Kocher Marc
 * @School BFH-TI Biel, Bachelor Thesis
 *
 */

package main.framework;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import main.framework.connection.ServerSocketFactory;
import main.framework.exception.PortListenerException;

/**
 * The class PortListenerManager is responsible for the port listeners management of the server.
 */
public class PortListenerManager {
	
	private ArrayList<PortListener> portListeners;
	private ServerSocketFactory serverSocketFactory;
	
	/**
	 * Creates a port listener manager including a server socket factory.
	 * 
	 * @param serverSockerFactory A server socket factory.
	 */
	public PortListenerManager(ServerSocketFactory serverSockerFactory) {
		this.serverSocketFactory = serverSockerFactory;
		this.portListeners = new ArrayList<>();
	}
	
	/**
	 * Attaches the given port listener to the port listener manager.
	 * 
	 * @param portListener The port listener.
	 * @throws PortListenerException If the port listener could not be attached because it is already attached.
	 */
	public void attachPortListener(PortListener portListener) throws PortListenerException {
		for (PortListener listener: this.portListeners) {
			if(portListener.getPort() == listener.getPort()){
				throw new PortListenerException("The port listener is already attached.");
			}
		}
		this.portListeners.add(portListener);
	}
	
	/**
	 * Detaches the given port listener from the port listener manager.
	 * 
	 * @param port The port number on which the port listener listens.
	 * @throws IOException If an I/O error occurs when closing the socket.
	 * @throws PortListenerException If the port listener could not be disabled because it is already disabled.
	 */
	public void detachPortListener(int port) throws IOException, PortListenerException {
		boolean successful = false;
		List<PortListener> portListeners = new ArrayList<>();
		portListeners.addAll(this.portListeners);
		for (PortListener portListener: portListeners) {
			if(portListener.getPort() == port){
				portListener.disable();
				this.portListeners.remove(portListener);
				successful = true;
				break;
			}
		}
		if(!successful){
			throw new PortListenerException("The port listener is already attached.");
		}
	}
	
	/**
	 * Returns the list with all attached port listeners.
	 * 
	 * @return All attached port listeners.
	 */
	public List<PortListener> getPortListeners() {
		return this.portListeners;
	}
	
	/**
	 * Returns the port listener corresponding to the given port number or null if no port listener with the given port was found.
	 * 
	 * @param port The port number.
	 * @return The corresponding port listener.
	 */
	public PortListener getPortListener(int port) {
		for (PortListener portListener: this.portListeners) {
			if(portListener.getPort() == port){
				return portListener;
			}
		}
		return null;
	}
	
	/**
	 * Enables all attached port listeners.
	 * 
	 * @throws KeyManagementException If the init operation fails.
	 * @throws NoSuchAlgorithmException If no Provider supports a TrustManagerFactorySpi implementation for the specified protocol.
	 * @throws PortListenerException If a port listener could not be enabled because it is already enabled.
	 * @throws IOException For networking errors.
	 */
	public void enablePortListeners() throws KeyManagementException, NoSuchAlgorithmException, PortListenerException, IOException {
		for (PortListener portListener: this.portListeners) {
			portListener.enable(this.serverSocketFactory.createServerSocket(portListener.getProtocol(), portListener.getPort()));
			new Thread(portListener).start();
		}
	}
	
	/**
	 * Disables all attached port listeners.
	 * 
	 * @throws IOException If an I/O error occurs when closing the socket.
	 * @throws PortListenerException If a port listener could not be disabled because it is already disabled.
	 */
	public void disablePortListeners() throws IOException, PortListenerException {
		for (PortListener portListener: this.portListeners) {
			portListener.disable();
		}
	}
	
	/**
	 * Enables the port listener corresponding to the given port number.
	 * 
	 * @param port The port number.
	 * @throws KeyManagementException If the init operation fails.
	 * @throws NoSuchAlgorithmException If no Provider supports a TrustManagerFactorySpi implementation for the specified protocol.
	 * @throws PortListenerException If the port listener could not be enabled because it is already enabled.
	 * @throws IOException For networking errors.

	 */
	public void enablePortListener(int port) throws KeyManagementException, NoSuchAlgorithmException, PortListenerException, IOException {
		for (PortListener portListener: this.portListeners) {
			if(portListener.getPort() == port){
				portListener.enable(this.serverSocketFactory.createServerSocket(portListener.getProtocol(), portListener.getPort()));
				break;
			}
		}
	}
	
	/**
	 * Disables the port listener corresponding to the given port number.
	 * 
	 * @param port The port number.
	 * @throws IOException If an I/O error occurs when closing the socket.
	 * @throws PortListenerException If the port listener could not be disabled because it is already disabled.
	 */
	public void disablePortListener(int port) throws IOException, PortListenerException {
		for (PortListener portListener: this.portListeners) {
			if(portListener.getPort() == port){
				portListener.disable();
				break;
			}
		}
	}
	
}
